﻿#region COPYRIGHT© 2009-2013 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using FlitBit.Parse;

namespace FlitBit.Json
{
	public class JsonObject : JsonValue, ICollection<JsonMember>
	{
		ConcurrentDictionary<string, JsonMember> _members = new ConcurrentDictionary<string, JsonMember>();

		public JsonObject()
			: base(JsonNodeKind.Object)
		{}

		public override TypeCode GetTypeCode()
		{
			return TypeCode.Object;
		}

		public override string ToString()
		{
			return new JsonFormatter().Write((JsonNode) this)
																.ToString();
		}

		void AddMember(Token<JsonTokenKind> name, JsonMember value)
		{
			if (!_members.TryAdd(name.Capture.Value, value))
			{
				throw new ParseException(String.Concat("Duplicate member: ", name.Capture.Value), name.Capture.Position);
			}
		}

		void Replace(JsonMember current, JsonMember replacement)
		{
			_members.TryUpdate(current.Name, replacement, current);
		}

		protected internal override void PerformBreadthFirstTransformFilter(Func<JsonNode, JsonNode> filter)
		{
			foreach (var m in _members.ToArray())
			{
				var n = filter(m.Value);
				if (!ReferenceEquals(n, m.Value))
				{
					if (n == null)
					{
						Remove(m.Value);
					}
					else if (n.Kind == JsonNodeKind.Member)
					{
						Replace(m.Value, n as JsonMember);
					}
					else if (n is JsonValue)
					{
						Replace(m.Value, JsonMember.MakeMember(m.Key, n as JsonValue));
					}
					else
					{
						throw new InvalidOperationException("A filter returned an invalid JsonNode, the member cannot be replaced.");
					}
				}
				else
				{
					m.Value.PerformBreadthFirstTransformFilter(filter);
				}
			}
		}

		protected internal override void PerformDepthFirstTransformFilter(Func<JsonNode, JsonNode> filter)
		{
			foreach (var m in _members.ToArray())
			{
				m.Value.PerformDepthFirstTransformFilter(filter);

				var n = filter(m.Value);
				if (!ReferenceEquals(n, m.Value))
				{
					if (n == null)
					{
						Remove(m.Value);
					}
					else if (n.Kind == JsonNodeKind.Member)
					{
						Replace(m.Value, n as JsonMember);
					}
					else if (n is JsonValue)
					{
						Replace(m.Value, JsonMember.MakeMember(m.Key, n as JsonValue));
					}
					else
					{
						throw new InvalidOperationException("A filter returned an invalid JsonNode, the member cannot be replaced.");
					}
				}
			}
		}

		protected internal override void WriteFormatted(JsonFormatter formatter)
		{
			formatter
				.OpenScope("{")
				.WriteMembers(from item in _members
											select item.Value)
				.CloseScope("}");
		}

		#region ICollection<JsonMember> Members

		public void Add(JsonMember item)
		{
			if (!_members.TryAdd(item.Name, item))
			{
				throw new InvalidOperationException(String.Concat("Duplicate member: ", item.Name));
			}
		}

		public void Clear()
		{
			_members.Clear();
		}

		public bool Contains(JsonMember item)
		{
			JsonMember member;
			if (_members.TryGetValue(item.Name, out member))
			{
				return EqualityComparer<JsonMember>.Default.Equals(item, member);
			}
			return false;
		}

		public void CopyTo(JsonMember[] array, int arrayIndex)
		{
			_members.Values.CopyTo(array, arrayIndex);
		}

		public int Count { get { return _members.Count; } }

		public bool IsReadOnly { get { return false; } }

		public bool Remove(JsonMember item)
		{
			JsonMember member;
			return _members.TryRemove(item.Name, out member);
		}

		public IEnumerator<JsonMember> GetEnumerator()
		{
			return _members.Values.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		#endregion

		internal static JsonObject ExpectObject(TokenizerState<JsonTokenKind> tokens)
		{
			tokens.ExpectTake(JsonTokenKind.Left_Curly_Brace_Symbol);
			var obj = new JsonObject();

			var look = tokens.ExpectAtAny(JsonTokenKind.Identifier, JsonTokenKind.Right_Curly_Brace_Symbol);
			if (look.Kind == JsonTokenKind.Right_Curly_Brace_Symbol)
			{
				tokens.MoveNext();
			}
			else
			{
				while (look.Kind != JsonTokenKind.Right_Curly_Brace_Symbol)
				{
					var name = tokens.ExpectTake(JsonTokenKind.Identifier);
					tokens.ExpectTake(JsonTokenKind.Colon_Symbol);

					var value = ExpectValue(tokens);
					obj.AddMember(name, JsonMember.MakeMember(name.Capture.Value, value));

					look = tokens.ExpectTakeAny(JsonTokenKind.Right_Curly_Brace_Symbol, JsonTokenKind.Comma_Symbol);
				}
			}
			return obj;
		}
	}
}